﻿module BJKParse
(* this module represents the BJK Core language parser interface.
   It allows parsing a string or an input file. *)
open System.Collections.Generic
open System.IO
open Microsoft.FSharp.Text.Lexing
open BJKParser
open BJKLexer

(* Parses a string. Returns a Core.Cmd - the AST matching the parsed program. *)
let aString(x : string) : Core.Cmd = 
    let lexbuf = LexBuffer<_>.FromString x
    BJKLexer.varString := ""
    BJKLexer.varIndex := 0
    BJKLexer.varTable.Clear()
    let y = try (BJKParser.PROG BJKLexer.tokenize) lexbuf 
            with e ->
                let pos = lexbuf.EndPos
                let line = pos.Line
                let column = pos.Column
                let message = e.Message
                let lastToken = new System.String(lexbuf.Lexeme)
                let () = Util.fail("Parse failed at line " + line.ToString() + ", column " + column.ToString() + "  :\nLast token: " + lastToken + "\n")
                Core.Skip
                   
    y
    
(* Parses a file. Returns Core.Cmd - the AST matching the parsed program. *)
let aFile(filename : string) : Core.Cmd =
    let text = File.ReadAllText(filename);
    aString(text)




 (* Goes over an AST and breaks all nested assignment commands to a sequence of regular assignment commands. 
    All other command types remain unchanged. *)
let astBreakNestedExpressions(ast) = 
    (* breaks an assignment command into a sequence of assignment commands which are semantically identical
       in term of effect on the store *)
    let breakAsgnNestedExpression(c : Core.Cmd,newVar : int) : Core.Cmd =      
        let rec bne(c : Core.Cmd) : Core.Cmd = 
            match c with
                | Core.Asgn(i,Core.Plus(Core.Zero, e)) -> bne(Core.Asgn(i, e))
                | Core.Asgn(i,Core.Plus(e, Core.Zero)) -> bne(Core.Asgn(i, e))     
                | Core.Asgn(i,Core.Plus(e, Core.Var y)) -> Core.Seq(bne(Core.Asgn(i,e)),
                                                                      Core.Asgn(i,Core.Plus(Core.Var i,Core.Var y)))
                | Core.Asgn(i,Core.Plus(Core.Var y, e)) -> Core.Seq(bne(Core.Asgn(i,e)),
                                                                      Core.Asgn(i,Core.Plus(Core.Var i,Core.Var y)))
                | Core.Asgn(i,Core.Times(e, Core.Var y)) -> Core.Seq(bne(Core.Asgn(i,e)),
                                                                       Core.Asgn(i,Core.Times(Core.Var i,Core.Var y)))
                | Core.Asgn(i,Core.Times(Core.Var y, e)) -> Core.Seq(bne(Core.Asgn(i,e)),
                                                                       Core.Asgn(i,Core.Times(Core.Var i,Core.Var y)))
                | Core.Asgn(i,Core.Times(e1, e2)) -> Core.Seq( Core.Seq( bne(Core.Asgn(i,e1)), bne(Core.Asgn(i+1,e2))),
                                                             Core.Asgn(i,Core.Times(Core.Var i, Core.Var (i+1))))
                | Core.Asgn(i,Core.Plus(e1, e2)) -> Core.Seq( Core.Seq( bne(Core.Asgn(i,e1)),bne(Core.Asgn(i+1,e2))),
                                                              Core.Asgn(i,Core.Plus(Core.Var i, Core.Var (i+1))))
                | _ -> c

        match c with
            | Core.Asgn(i,Core.Zero) -> c
            | Core.Asgn(i,Core.Var _) -> c
            | Core.Asgn(i,Core.Times(Core.Zero, _)) -> Core.Asgn(i, Core.Zero)
            | Core.Asgn(i,Core.Times(_, Core.Zero)) -> Core.Asgn(i, Core.Zero)     
            | Core.Asgn(i,Core.Plus(Core.Var _, Core.Var _)) -> c
            | Core.Asgn(i,Core.Times(Core.Var _, Core.Var _)) -> c
            | Core.Asgn(i,e) -> Core.Seq(bne(Core.Asgn(newVar,e)),Core.Asgn(i,Core.Var newVar))
            | _ -> c     
    (* goes over an AST and breaks all nested assignment commands to a sequence of regular assignment commands.
      'hv' is a variable index greater than all variable indices referenced in any of the handled expressions. *)
    let rec breakNestedExpressions = function
        | (Core.Skip,hv) -> Core.Skip
        | (Core.Seq(c1,c2),hv) -> Core.Seq(breakNestedExpressions(c1,hv),breakNestedExpressions(c2,hv))
        | (Core.Loop(i,c),hv) -> Core.Loop(i,breakNestedExpressions(c,hv))
        | (Core.Choice(c1,c2),hv) -> Core.Choice(breakNestedExpressions(c1,hv),breakNestedExpressions(c2,hv))
        | (cmd,hv) -> breakAsgnNestedExpression(cmd, hv);

    breakNestedExpressions(ast,1 + Core.highest_var(ast))
    
(* a function for printing of the variable name to index mapping which was generated by the last call to 'parse' *)
let parserLastVarMapping() = !(BJKLexer.varString)
let parserLastVarToIndexMap() = 
    let vt = BJKLexer.varTable
    let vt' = new Dictionary<int, string>() 
    for k in BJKLexer.varTable.Keys do
        vt'.Add(vt.[k], k)
    vt'


        

